package com.ehsunbehravesh.mypasswords.gui;

import com.ehsunbehravesh.mypasswords.Domain;
import com.ehsunbehravesh.mypasswords.Logger;
import com.ehsunbehravesh.mypasswords.Utils;
import com.ehsunbehravesh.mypasswords.resource.ResourceLoader;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.security.NoSuchAlgorithmException;
import javax.swing.*;

public class LoginDialog extends javax.swing.JDialog {

  public static final int LOGIN_SUCCESSFULL = 1;
  public static final int LOGIN_FAILED = 2;
  public static final int LOGIN_CANCELED = 3;
  private static final int ALLOWED_LOGINS = 5;
  private static final int LOCK_INTERVAL = 30; // seconds
  private int loginResult;
  private JFileChooser openDialog;
  private int failedLogins;
  private boolean locked;
  private Timer tmrDisplayLock, tmrOpenLock;
  private int remainingSeconds;

  public LoginDialog(JFrame parent, boolean modal, boolean centerOfParent) {
    super(parent, modal);
    initComponents();
    myInitComponente();
    if (centerOfParent) {
      Utils.setCenterOfParent(parent, this);
    } else {
      setCenter();
    }
  }

  @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        pnlMain = new javax.swing.JPanel();
        jLabel1 = new javax.swing.JLabel();
        txtPassword = new javax.swing.JPasswordField();
        btnCancel = new javax.swing.JButton();
        btnOk = new javax.swing.JButton();
        lblIcon = new javax.swing.JLabel();
        btnRecover = new javax.swing.JButton();

        setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE);
        setResizable(false);
        addWindowListener(new java.awt.event.WindowAdapter() {
            public void windowClosing(java.awt.event.WindowEvent evt) {
                formWindowClosing(evt);
            }
        });

        pnlMain.setBackground(new java.awt.Color(254, 254, 254));

        jLabel1.setText("Please enter your password:");

        txtPassword.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyPressed(java.awt.event.KeyEvent evt) {
                txtPasswordKeyPressed(evt);
            }
        });

        btnCancel.setText("Cancel");
        btnCancel.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnCancelActionPerformed(evt);
            }
        });
        btnCancel.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyPressed(java.awt.event.KeyEvent evt) {
                btnCancelKeyPressed(evt);
            }
        });

        btnOk.setText("OK");
        btnOk.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnOkActionPerformed(evt);
            }
        });
        btnOk.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyPressed(java.awt.event.KeyEvent evt) {
                btnOkKeyPressed(evt);
            }
        });

        btnRecover.setText("Login by Recovery File");
        btnRecover.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnRecoverActionPerformed(evt);
            }
        });
        btnRecover.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyPressed(java.awt.event.KeyEvent evt) {
                btnRecoverKeyPressed(evt);
            }
        });

        javax.swing.GroupLayout pnlMainLayout = new javax.swing.GroupLayout(pnlMain);
        pnlMain.setLayout(pnlMainLayout);
        pnlMainLayout.setHorizontalGroup(
            pnlMainLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, pnlMainLayout.createSequentialGroup()
                .addContainerGap()
                .addComponent(lblIcon, javax.swing.GroupLayout.PREFERRED_SIZE, 64, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addGroup(pnlMainLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(btnRecover, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 220, Short.MAX_VALUE)
                    .addComponent(jLabel1, javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(javax.swing.GroupLayout.Alignment.LEADING, pnlMainLayout.createSequentialGroup()
                        .addComponent(btnOk, javax.swing.GroupLayout.PREFERRED_SIZE, 89, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 42, Short.MAX_VALUE)
                        .addComponent(btnCancel, javax.swing.GroupLayout.PREFERRED_SIZE, 89, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addComponent(txtPassword, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 220, Short.MAX_VALUE))
                .addGap(37, 37, 37))
        );
        pnlMainLayout.setVerticalGroup(
            pnlMainLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(pnlMainLayout.createSequentialGroup()
                .addGap(18, 18, 18)
                .addGroup(pnlMainLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(pnlMainLayout.createSequentialGroup()
                        .addComponent(jLabel1)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(txtPassword, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addGap(18, 18, 18)
                        .addGroup(pnlMainLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(btnOk)
                            .addComponent(btnCancel)))
                    .addComponent(lblIcon, javax.swing.GroupLayout.PREFERRED_SIZE, 64, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addComponent(btnRecover)
                .addContainerGap(20, Short.MAX_VALUE))
        );

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(pnlMain, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(pnlMain, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

  private void txtPasswordKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_txtPasswordKeyPressed
    if (evt.getKeyCode() == 10) {
      btnOkActionPerformed(null);
    } else if (evt.getKeyCode() == 27) {
      btnCancelActionPerformed(null);
    }
}//GEN-LAST:event_txtPasswordKeyPressed

  private void btnCancelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnCancelActionPerformed
    setVisible(false);
    dispose();
    loginResult = LOGIN_CANCELED;
}//GEN-LAST:event_btnCancelActionPerformed

  private void btnOkActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnOkActionPerformed
    if (locked) {
      return;
    }
    try {
      Domain domain = Domain.getInstance();
      String currentPassword = domain.getSetting("password", null);
      currentPassword = currentPassword != null ? currentPassword : "";
      String enteredPassword = new String(txtPassword.getPassword());
      enteredPassword = Utils.SHA256(enteredPassword);

      if (currentPassword.equals(enteredPassword)) {
        setVisible(false);
        dispose();
        loginResult = LOGIN_SUCCESSFULL;
        domain.setPassword(new String(txtPassword.getPassword()));
      } else {
        JOptionPane.showMessageDialog(this, "Incorrect password!", "Warning", JOptionPane.WARNING_MESSAGE);
        txtPassword.selectAll();
        txtPassword.requestFocus();

        String value = domain.getSetting("log_failed_logins", null);

        if (value != null) {
          int intValue = Integer.parseInt(value);
          if (intValue < 1000000) {
            Logger.log("Failed login. Password: " + new String(txtPassword.getPassword()));
          }
        } else {
          Logger.log("Failed login. Password: " + new String(txtPassword.getPassword()));
        }

        if (++failedLogins >= ALLOWED_LOGINS) {
          locked = true;
          btnOk.setEnabled(false);
          tmrOpenLock = new Timer(1000 * LOCK_INTERVAL, new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent ae) {
              tmrOpenLockActionPerformed(ae);
            }
          });
          tmrOpenLock.setRepeats(false);
          tmrOpenLock.start();
          remainingSeconds = LOCK_INTERVAL;

          changeBtnOKText(remainingSeconds);

          tmrDisplayLock = new Timer(1000, new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent ae) {
              tmrDisplayLockActionPerformed(ae);
            }
          });
          
          tmrDisplayLock.setRepeats(true);
          tmrDisplayLock.start();
        }
      }
    } catch (NoSuchAlgorithmException ex) {
      Logger.log("Failed to hash password: " + ex.getMessage());
    }
}//GEN-LAST:event_btnOkActionPerformed

  private void formWindowClosing(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosing
    setVisible(false);
    dispose();
    loginResult = LOGIN_CANCELED;
  }//GEN-LAST:event_formWindowClosing

  private void btnRecoverActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnRecoverActionPerformed
    btnRecoverActionPerform(evt);
  }//GEN-LAST:event_btnRecoverActionPerformed

  private void btnCancelKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_btnCancelKeyPressed
    if (evt.getKeyCode() == 10) {
      btnOkActionPerformed(null);
    } else if (evt.getKeyCode() == 27) {
      btnCancelActionPerformed(null);
    }
  }//GEN-LAST:event_btnCancelKeyPressed

  private void btnOkKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_btnOkKeyPressed
    if (evt.getKeyCode() == 10) {
      btnOkActionPerformed(null);
    } else if (evt.getKeyCode() == 27) {
      btnCancelActionPerformed(null);
    }
  }//GEN-LAST:event_btnOkKeyPressed

  private void btnRecoverKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_btnRecoverKeyPressed
    if (evt.getKeyCode() == 10) {
      btnOkActionPerformed(null);
    } else if (evt.getKeyCode() == 27) {
      btnCancelActionPerformed(null);
    }
  }//GEN-LAST:event_btnRecoverKeyPressed

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton btnCancel;
    private javax.swing.JButton btnOk;
    private javax.swing.JButton btnRecover;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel lblIcon;
    private javax.swing.JPanel pnlMain;
    private javax.swing.JPasswordField txtPassword;
    // End of variables declaration//GEN-END:variables

  private void myInitComponente() {
    try {
      URL url = ResourceLoader.class.getResource("password.png");
      lblIcon.setIcon(new ImageIcon(url));
      txtPassword.requestFocus();
    } catch (Exception ex) {
      Logger.log(ex.getMessage());
    }
  }

  public int getLoginResult() {
    return loginResult;
  }

  private void setCenter() {
    Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
    setLocation(screenSize.width / 2 - getWidth() / 2, screenSize.height / 2 - getHeight() / 2);
  }

  private void btnRecoverActionPerform(ActionEvent evt) {
    if (openDialog == null) {
      openDialog = new JFileChooser();
    }
    int answer = openDialog.showOpenDialog(this);

    if (answer != JFileChooser.CANCEL_OPTION) {
      File recoveryFile = openDialog.getSelectedFile();
      BufferedReader reader = null;
      try {
        reader = new BufferedReader(new FileReader(recoveryFile));
        String token = reader.readLine();
        String password = Utils.recoverPassword(token);

        txtPassword.setText(password);
      } catch (Exception ex) {
        Logger.log(ex.getMessage());
        JOptionPane.showMessageDialog(this, "Password recovery failed!", "Error", JOptionPane.ERROR_MESSAGE);
      } finally {
        if (reader != null) {
          try {
            reader.close();
          } catch (IOException ex) {
            Logger.log(ex.getMessage());
          }
        }
      }
    }
  }

  private void tmrOpenLockActionPerformed(ActionEvent ae) {
    locked = false;
    btnOk.setEnabled(true);
    failedLogins = 0;
    tmrDisplayLock.stop();
    btnOk.setText("OK");
  }

  private void changeBtnOKText(int remainingSeconds) {
    btnOk.setText("OK (" + remainingSeconds + ")");
  }

  private void tmrDisplayLockActionPerformed(ActionEvent ae) {
    remainingSeconds--;
    changeBtnOKText(remainingSeconds);
  }
}
